from .entity import Entity

"""
Basic data structure used for general trading function in VN Trader.
"""

from dataclasses import dataclass
from datetime import datetime
from logging import INFO

from BQ.utils.constant import Direction, Exchange, Interval, Offset, Status, Product, OptionType, OrderPriceType

ACTIVE_STATUSES = {Status.SUBMITTING, Status.NOTTRADED, Status.PARTTRADED}


@dataclass
class BaseData(Entity):
    """
    Any data object needs a gateway_name as source
    and should inherit base data.
    """

    gateway_name: str 
    raw_data = None


@dataclass
class TickData(BaseData):
    """
    Tick data contains information about:
        * last trade in market
        * orderbook snapshot
        * intraday market statistics.
    """

    symbol: str
    exchange: Exchange
    datetime: datetime
    actionday: str = ''
    tradingday: str = ''
    updatetime: str = ''
    updatemillisec: int = 0

    name: str = ""
    volume: int = 0
    open_interest: int = 0
    last_price: float = 0
    last_volume: int = 0
    limit_up: float = 0
    limit_down: float = 0

    open_price: float = 0
    high_price: float = 0
    low_price: float = 0
    pre_close: float = 0

    bid_price_1: float = 0
    bid_price_2: float = 0
    bid_price_3: float = 0
    bid_price_4: float = 0
    bid_price_5: float = 0

    ask_price_1: float = 0
    ask_price_2: float = 0
    ask_price_3: float = 0
    ask_price_4: float = 0
    ask_price_5: float = 0

    bid_volume_1: int = 0
    bid_volume_2: int = 0
    bid_volume_3: int = 0
    bid_volume_4: int = 0
    bid_volume_5: int = 0

    ask_volume_1: int = 0
    ask_volume_2: int = 0
    ask_volume_3: int = 0
    ask_volume_4: int = 0
    ask_volume_5: int = 0

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange.value if isinstance(self.exchange, Exchange) else self.exchange}"

    def from_tq(self, tick):
        """
       天 tick 结构如下:

{
    "datetime": "",  # "2017-07-26 23:04:21.000001" (行情从交易所发出的时间(北京时间))
    "ask_price5": float("nan"),  # 6122.0 (卖五价)
    "ask_volume5": 0,  # 3 (卖五量)
    "ask_price4": float("nan"),  # 6122.0 (卖四价)
    "ask_volume4": 0,  # 3 (卖四量)
    "ask_price3": float("nan"),  # 6122.0 (卖三价)
    "ask_volume3": 0,  # 3 (卖三量)
    "ask_price2": float("nan"),  # 6122.0 (卖二价)
    "ask_volume2": 0,  # 3 (卖二量)
    "ask_price1": float("nan"),  # 6122.0 (卖一价)
    "ask_volume1": 0,  # 3 (卖一量)
    "bid_price1": float("nan"),  # 6121.0 (买一价)
    "bid_volume1": 0,  # 7 (买一量)
    "bid_price2": float("nan"),  # 6121.0 (买二价)
    "bid_volume2": 0,  # 7 (买二量)
    "bid_price3": float("nan"),  # 6121.0 (买三价)
    "bid_volume3": 0,  # 7 (买三量)
    "bid_price4": float("nan"),  # 6121.0 (买四价)
    "bid_volume4": 0,  # 7 (买四量)
    "bid_price5": float("nan"),  # 6121.0 (买五价)
    "bid_volume5": 0,  # 7 (买五量)
    "last_price": float("nan"),  # 6122.0 (最新价)
    "highest": float("nan"),  # 6129.0 (当日最高价)
    "lowest": float("nan"),  # 6101.0 (当日最低价)
    "open": float("nan"),  # 6102.0 (开盘价)
    "close": float("nan"),  # nan (收盘价)
    "average": float("nan"),  # 6119.0 (当日均价)
    "volume": 0,  # 89252 (成交量)
    "amount": float("nan"),  # 5461329880.0 (成交额)
    "open_interest": 0,  # 616424 (持仓量)
    "settlement": float("nan"),  # nan (结算价)
    "upper_limit": float("nan"),  # 6388.0 (涨停价)
    "lower_limit": float("nan"),  # 5896.0 (跌停价)
    "pre_open_interest": 0,  # 616620 (昨持仓量)
    "pre_settlement": float("nan"),  # 6142.0 (昨结算价)
    "pre_close": float("nan"),  # 6106.0 (昨收盘价)
    "price_tick": float("nan"),  # 10.0 (合约价格单位)
    "price_decs": 0,  # 0 (合约价格小数位数)
    "volume_multiple": 0,  # 10 (合约乘数)
    "max_limit_order_volume": 0,  # 500 (最大限价单手数)
    "max_market_order_volume": 0,  # 0 (最大市价单手数)
    "min_limit_order_volume": 0,  # 1 (最小限价单手数)
    "min_market_order_volume": 0,  # 0 (最小市价单手数)
    "underlying_symbol": "",  # SHFE.rb1901 (标的合约)
    "strike_price": float("nan"),  # nan (行权价)
    "change": float("nan"),  # −20.0 (涨跌)
    "change_percent": float("nan"),  # −0.00325 (涨跌幅)
    "expired": False,  # False (合约是否已下市)
}

        """
        for k, v in TickData.__dict__.items():
            if k in tick:
                setattr(self, k, v)
        self.limit_down = tick.get('lower_limit', 0)
        self.limit_up = tick.get('upper_limit', 0)
        self.tq_symbol = tick.get('underlying_symbol', '')


@dataclass
class BarData(BaseData):
    """
    Candlestick bar data of a certain trading period.
    """

    symbol: str
    exchange: Exchange
    datetime: datetime

    interval: Interval = None
    volume: int = 0
    open_interest: int = 0
    open_price: float = 0
    high_price: float = 0
    low_price: float = 0
    close_price: float = 0
    actionday: str = ''
    tradingday: str = ''

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange.value if isinstance(self.exchange, Exchange) else self.exchange}"

    def from_tq(self, bar):
        """get_kline_serial() 的返回值是一个 pandas.DataFrame, 包含以下列:
        id: 1234 (k线序列号)
        datetime: 1501080715000000000 (K线起点时间(按北京时间)，自unix epoch(1970-01-01 00:00:00 GMT)以来的纳秒数)
        open: 51450.0 (K线起始时刻的最新价)
        high: 51450.0 (K线时间范围内的最高价)
        low: 51450.0 (K线时间范围内的最低价)
        close: 51450.0 (K线结束时刻的最新价)
        volume: 11 (K线时间范围内的成交量)
        open_oi: 27354 (K线起始时刻的持仓量)
        close_oi: 27355 (K线结束时刻的持仓量)

        """
        dt = nano_to_str(bar['datetime'])
        for k, v in BarData.__dict__.items():
            if k in bar.index:
                setattr(self, k, bar[k])
        self.datetime = dt
        self.tq_symbol = bar.get('symbol')


def nano_to_str(nano):
    dt = datetime.fromtimestamp(nano // 1000000000)
    s = dt.strftime('%Y-%m-%d %H:%M:%S')
    s += '.' + str(int(nano % 1000000000)).zfill(9)
    return s


@dataclass
class OrderData(BaseData):
    """
    Order data contains information for tracking lastest status
    of a specific order.
    """

    symbol: str
    exchange: Exchange
    orderid: str

    order_type: OrderPriceType = OrderPriceType.LIMIT
    direction: Direction = ""
    offset: Offset = Offset.NONE
    price: float = 0
    volume: float = 0
    traded: float = 0
    status: Status = Status.SUBMITTING
    time: str = ""

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"
        self.vt_orderid = f"{self.gateway_name}.{self.orderid}"

    def is_active(self):
        """
        Check if the order is active.
        """
        if self.status in ACTIVE_STATUSES:
            return True
        else:
            return False

    def create_cancel_request(self):
        """
        Create cancel request object from order.
        """
        req = CancelRequest(
            orderid=self.orderid, symbol=self.symbol, exchange=self.exchange
        )
        return req

    def from_tq_order(self, tqo):
        """self._api = api
        #: 委托单ID, 对于一个用户的所有委托单，这个ID都是不重复的
        self.order_id = ""
        #: 交易所单号
        self.exchange_order_id = ""
        #: 交易所
        self.exchange_id = ""
        #: 交易所内的合约代码
        self.instrument_id = ""
        #: 下单方向, BUY=买, SELL=卖
        self.direction = ""
        #: 开平标志, OPEN=开仓, CLOSE=平仓, CLOSETODAY=平今
        self.offset = ""
        #: 总报单手数
        self.volume_orign = 0
        #: 未成交手数
        self.volume_left = 0
        #: 委托价格, 仅当 price_type = LIMIT 时有效
        self.limit_price = float("nan")
        #: 价格类型, ANY=市价, LIMIT=限价
        self.price_type = ""
        #: 手数条件, ANY=任何数量, MIN=最小数量, ALL=全部数量
        self.volume_condition = ""
        #: 时间条件, IOC=立即完成，否则撤销, GFS=本节有效, GFD=当日有效, GTC=撤销前有效, GFA=集合竞价有效
        self.time_condition = ""
        #: 下单时间，自unix epoch(1970-01-01 00:00:00 GMT)以来的纳秒数.
        self.insert_date_time = 0
        #: 委托单状态信息
        self.last_msg = ""
        #: 委托单状态, ALIVE=有效, FINISHED=已完
        self.status = ""
        self._this_session = False
"""


@dataclass
class TradeData(BaseData):
    """
    Trade data contains information of a fill of an order. One order
    can have several trade fills.
    """

    symbol: str
    exchange: Exchange
    orderid: str
    tradeid: str
    direction: Direction = ""

    offset: Offset = Offset.NONE
    price: float = 0
    volume: float = 0
    time: str = ""

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"
        self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
        self.vt_tradeid = f"{self.gateway_name}.{self.tradeid}"


@dataclass
class PositionData(BaseData):
    """
    Positon data is used for tracking each individual position holding.
    """

    symbol: str
    exchange: Exchange
    direction: Direction

    volume: float = 0
    frozen: float = 0
    price: float = 0
    pnl: float = 0
    yd_volume: float = 0

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"
        self.vt_positionid = f"{self.vt_symbol}.{self.direction.value}"


@dataclass
class PositionDetailData(BaseData):
    symbol: str
    exchange: Exchange
    direction: Direction
    trade_id: str
    trade_datetime: str
    volume: float = 0
    price: float = 0
    investor_id: str = ''
    broker_id: str = ''
    position_profit: float = 0

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"
        self.vt_positionid = f"{self.vt_symbol}|{self.trade_datetime}|{self.direction.value}|{self.trade_id}"


@dataclass
class AccountData(BaseData):
    """
    Account data contains information about balance, frozen and
    available.
    """

    accountid: str

    balance: float = 0
    frozen: float = 0

    def __post_init__(self):
        """"""
        self.available = self.balance - self.frozen
        self.vt_accountid = f"{self.gateway_name}.{self.accountid}"


@dataclass
class LogData(BaseData):
    """
    Log data is used for recording log messages on GUI or in log files.
    """

    msg: str
    level: int = INFO

    def __post_init__(self):
        """"""
        self.time = datetime.now()


@dataclass
class ContractData(BaseData):
    """
    Contract data contains basic information about each contract traded.
    """

    symbol: str
    exchange: Exchange
    name: str
    product: Product
    size: int
    pricetick: float

    min_volume: float = 1  # minimum trading volume of the contract
    stop_supported: bool = False  # whether server supports stop order
    net_position: bool = False  # whether gateway uses net position volume
    history_data: bool = False  # whether gateway provides bar history data

    option_strike: float = 0
    option_underlying: str = ""  # vt_symbol of underlying contract
    option_type: OptionType = None
    option_expiry: datetime = None

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"

    @property
    def tq_symbol(self):
        return f'{self.exchange.value}.{self.symbol}' if isinstance(self.exchange, Exchange) else '.'.join(
            [self.exchange, self.symbol])


@dataclass
class SubscribeRequest:
    """
    Request sending to specific gateway for subscribing tick data update.
    """

    symbol: str
    exchange: Exchange

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"


@dataclass
class OrderRequest:
    """
    Request sending to specific gateway for creating a new order.
    """

    symbol: str
    exchange: Exchange
    direction: Direction
    order_type: OrderPriceType
    volume: float
    price: float = 0
    offset: Offset = Offset.NONE

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange.value if isinstance(self.exchange, Exchange) else self.exchange}"

    def create_order_data(self, orderid: str, gateway_name: str):
        """
        Create order data from request.
        """
        order = OrderData(
            symbol=self.symbol,
            exchange=self.exchange,
            orderid=orderid,
            order_type=self.order_type,
            direction=self.direction,
            offset=self.offset,
            price=self.price,
            volume=self.volume,
            gateway_name=gateway_name,
        )
        return order


@dataclass
class CancelRequest:
    """
    Request sending to specific gateway for canceling an existing order.
    """

    orderid: str
    symbol: str
    exchange: Exchange

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"


@dataclass
class HistoryRequest:
    """
    Request sending to specific gateway for querying history data.
    """

    symbol: str
    exchange: Exchange
    start: datetime
    end: datetime = None
    interval: Interval = None

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange if isinstance(self.exchange, str) else self.exchange.value}"
